home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / mac / macconq.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-02  |  55.0 KB  |  2,280 lines  |  [TEXT/KAHL]

  1. /* Main program of the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11. extern int do_key_down_construction(int);
  12.  
  13. #include <stdarg.h>
  14. #include <errors.h>
  15. #include <time.h>
  16.  
  17. /* The console is a useful debugging hack.  When not in use, anything that uses stdout
  18.    should be disabled. */
  19.  
  20. #ifdef USE_CONSOLE
  21. #ifdef THINK_C
  22. #include <console.h>
  23. #include <Folders.h>
  24. #else
  25. #ifndef printf
  26. printf() {}
  27. #endif
  28. #endif
  29. #endif /* USE_CONSOLE */
  30.  
  31. #define keyReplyErr 'errn'
  32.  
  33. /* Function prototypes. */
  34.  
  35. static pascal OSErr do_ae_open_application(AppleEvent *message, AppleEvent *reply, long refcon);
  36. static pascal OSErr do_ae_open_documents(AppleEvent *message, AppleEvent *reply, long refcon);
  37. static pascal OSErr do_ae_print_documents(AppleEvent *message, AppleEvent *reply, long refcon);
  38. static pascal OSErr do_ae_quit_application(AppleEvent *message, AppleEvent *reply, long refcon);
  39. static pascal OSErr do_ae_join_game(AppleEvent *message, AppleEvent *reply, long refcon);
  40.  
  41. static Boolean missed_any_parameters(AppleEvent *message);
  42.  
  43. /* Global variables. */
  44.  
  45. /* This is the id of any map modal tool currently in effect. */
  46.  
  47. int map_modal = 0;
  48.  
  49. int inbackground;
  50.  
  51. /* This is the list of maps that we're using. */
  52.  
  53. struct a_map *maplist;
  54.  
  55. /* This is the list of lists. */
  56.  
  57. struct a_list *listlist;
  58.  
  59. /* This is the list of unit closeups. */
  60.  
  61. struct a_unit_closeup *unitcloseuplist;
  62.  
  63. /* This indicates whether the general game resource files was found. */
  64.  
  65. int foundresourcesfile = FALSE;
  66.  
  67. /* This flag indicates whether the image etc resource file(s) were found. */
  68.  
  69. int foundimagesfile = FALSE;
  70.  
  71. /* The usual width of a scrollbar. */
  72.  
  73. int sbarwid = 15;
  74.  
  75. /* True if new maps should show the cell grid. */
  76.  
  77. int default_draw_grid = TRUE;
  78.  
  79. /* True if new maps should display names and numbers. */
  80.  
  81. int default_draw_names = FALSE;
  82.  
  83. /* (should use these eventually instead) */
  84.  
  85. int default_draw_unit_names = FALSE;
  86. int default_draw_unit_numbers = FALSE;
  87. int default_draw_feature_names = FALSE;
  88.  
  89. /* True if we're going to use WaitNextEvent. */
  90.  
  91. int useWNE = FALSE;
  92.  
  93. /* Rectangle that constrains window dragging. */
  94.  
  95. Rect dragrect;
  96.  
  97. /* Rectangle that constrains window resizing. */
  98.  
  99. Rect sizerect;
  100.  
  101. /* This is the side that is using this Mac as its display. */
  102.  
  103. Side *dside = NULL;
  104.  
  105. /* This points to a spare block of memory that is freed so shutdown code can
  106.    use it (no guarantee that it will tho). */
  107.  
  108. Handle spare;
  109.  
  110. /* This is true if savable prefs etc have been saved since being changed. */
  111. /* (always true for now, nothing being remembered) */
  112.  
  113. int interfacestatesafe = TRUE;
  114.  
  115. /* This is true when a single click suffices to move a unit. */
  116.  
  117. int defaultmoveonclick = TRUE;
  118.  
  119. int defaultautoselect = TRUE;
  120.  
  121. int wasingame = TRUE;
  122.  
  123. int suppresswarnings = FALSE;
  124.  
  125. int playsounds = TRUE;
  126.  
  127. /* Set to true if Color QuickDraw is installed. */
  128.  
  129. int hasColorQD;
  130.  
  131. /* The range of screen pixel depths that the display has to cope with. */
  132.  
  133. int minscreendepth = -1;
  134.  
  135. int maxscreendepth = -1;
  136.  
  137. /* This is true if AppleEvents are available. */
  138.  
  139. int hasAppleEvents;
  140.  
  141. /* This is true if the PPC toolbox is available. */
  142.  
  143. int hasPPCToolbox;
  144.  
  145. char *curdatestr = NULL;
  146.  
  147. int eventloopdone = FALSE;
  148.  
  149. int inputinvalid = FALSE;
  150.  
  151. /* True when a won or lost dialog has been put up on the screen already. */
  152.  
  153. static int told_outcome = FALSE;
  154.  
  155. /* The main Mac program. */
  156.  
  157. int
  158. main()
  159. {
  160.     /* Do the most basic Macintosh setup. */
  161.     init_toolbox();
  162.     init_cursors();
  163.     init_patterns();
  164.     init_icons();
  165.     init_menus();
  166.     init_rects();
  167.     init_ae();
  168. #ifdef USE_CONSOLE
  169.     /* Make sure the console window is up before anybody tries to write to it. */
  170.     freopenc(NULL, stdout);
  171.     freopenc(stdout, stderr);
  172. #endif /* USE_CONSOLE */
  173.     /* Put the Xconq kernel into a known state. */
  174.     clear_game_modules();
  175.     init_data_structures();
  176.     init_library_path(NULL);
  177.     /* Acquire Mac-specific files (preferences and resources). */
  178.     get_files();
  179.     /* A hack to ensure some memory available for error handling. */
  180.     spare = NewHandle(2000);
  181.     /* If no Apple Events, go to the splash screen now, otherwise we'll wait
  182.        for an oapp/odoc/pdoc event to decide what to do. */
  183.     if (!hasAppleEvents) {
  184.         if (splash_dialog() == diSplashQuit)
  185.           return 0;
  186.     }
  187.     /* All essential init done, jump into the main event loop. */
  188.     event_loop();
  189.     /* Will exit here, or perhaps via error. */
  190.     return 0;
  191. }
  192.  
  193. /* This is the first dialog that the user sees.  It doesn't do much
  194.    besides provide the initial choice into the program proper. */
  195.  
  196. int
  197. splash_dialog()
  198. {
  199.     switch (do_splash_box()) {
  200.         case diSplashNew:
  201.             new_game_dialog();
  202.             break;
  203.         case diSplashOpen:
  204.             open_game_dialog();
  205.             break;
  206.         case diSplashConnect:
  207.             connect_game_dialog();
  208.             break;
  209.         case diSplashQuit:
  210.             return diSplashQuit;
  211.     }
  212.     return -1;
  213. }
  214.  
  215. /* (just a place holder for now) */
  216.  
  217. void
  218. connect_game_dialog()
  219. {
  220.     LocationNameRec foo;
  221.     PortInfoRec bar;
  222.     
  223.     PPCBrowser("\pChoose an Xconq game to join:",
  224.                "\pXconq", 0, &foo, &bar, NULL, "\p?");
  225. }
  226.  
  227. /* Do the usual Mac setup calls. */
  228.  
  229. void
  230. init_toolbox()
  231. {
  232.     SysEnvRec se;
  233.  
  234.     MaxApplZone();
  235.  
  236.     InitGraf(&QD(thePort));
  237.     InitFonts();
  238.     FlushEvents(everyEvent, 0);
  239.     InitWindows();
  240.     InitMenus();
  241.     TEInit();
  242.     InitDialogs(NULL);
  243.     InitCursor();
  244.  
  245.     SysEnvirons(2, &se);
  246.     hasColorQD = se.hasColorQD;
  247.     DGprintf("%s Color QuickDraw\n", (hasColorQD ? "Using" : "Not using"));
  248.     recalc_depths();
  249. }
  250.  
  251. /* Look at all the devices and compute the range of screen depths. */
  252.  
  253. void
  254. recalc_depths()
  255. {
  256.     int depth, oldmin = minscreendepth, oldmax = maxscreendepth;
  257.     GDHandle gdev;
  258.  
  259.     if (hasColorQD) {
  260.         gdev = GetDeviceList();
  261.         minscreendepth = maxscreendepth = (*((*gdev)->gdPMap))->pixelSize;
  262.         while ((gdev = GetNextDevice(gdev)) != nil) {
  263.             depth = (*((*gdev)->gdPMap))->pixelSize;
  264.             if (depth < minscreendepth) minscreendepth = depth;
  265.             if (depth > maxscreendepth) maxscreendepth = depth;
  266.         }
  267.     } else {
  268.         minscreendepth = maxscreendepth = 1;
  269.     }
  270.     if (minscreendepth != oldmin || maxscreendepth != oldmax) {
  271.         DGprintf("Screen depths range from %d to %d\n", minscreendepth, maxscreendepth);
  272.     }
  273. }
  274.  
  275. /* Set up the generic dragging and sizing rects. */
  276.  
  277. void
  278. init_rects()
  279. {
  280.     RgnHandle screenrgn;
  281.  
  282.     screenrgn = GetGrayRgn();
  283.     dragrect = (*screenrgn)->rgnBBox;
  284.     SetRect(&sizerect, 50, 50, (*screenrgn)->rgnBBox.right,  (*screenrgn)->rgnBBox.bottom);
  285. }
  286.  
  287. /* Basic Apple Event handling. */
  288.  
  289. void
  290. init_ae()
  291. {
  292.     OSErr err;
  293.     long rslt;
  294.  
  295.     hasPPCToolbox  = (Gestalt(gestaltPPCToolboxAttr, &rslt) ? false : (rslt != 0));
  296.     hasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &rslt) ? false : (rslt != 0));
  297.  
  298.     if (hasAppleEvents) {
  299.         err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  300.                                     (ProcPtr) do_ae_open_application, 0L, false);
  301.         if (err) {
  302. /*                Alert(rErrorAlert, nil);  */
  303.                 beep();
  304.                 return;
  305.         }
  306.         err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  307.                                     (ProcPtr) do_ae_open_documents, 0L, false);
  308.         if (err) {
  309. /*                Alert(rErrorAlert, nil);  */
  310.                 beep();
  311.                 return;
  312.         }
  313.         err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
  314.                                     (ProcPtr) do_ae_print_documents, 0L, false);
  315.         if (err) {
  316. /*                Alert(rErrorAlert, nil);  */
  317.                 beep();
  318.                 return;
  319.         }
  320.         err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  321.                                     (ProcPtr) do_ae_quit_application, 0L, false);
  322.         if (err) {
  323. /*                Alert(rErrorAlert, nil);  */
  324.                 beep();
  325.                 return;
  326.         }
  327.         err = AEInstallEventHandler('xcnq', 'join',
  328.                                     (ProcPtr) do_ae_join_game, 0L, false);
  329.         if (err) {
  330. /*                Alert(rErrorAlert, nil);  */
  331.                 beep();
  332.                 return;
  333.         }
  334.     }
  335. }
  336.  
  337. short prefs_refnum = -1;
  338.  
  339. /* Open and/or load any files that we might need, such as preferences
  340.    and resources. */
  341.  
  342. void
  343. get_files()
  344. {
  345.     Str255 filename;
  346.     extern short initialvrefnum;
  347.  
  348.     /* Capture the current vrefnum. */
  349.     GetVol(NULL, &initialvrefnum);
  350.     /* Load up any preferences. */
  351.     get_preferences();
  352.     /* Look for and open game library resource files. */
  353.     foundresourcesfile = FALSE;
  354.     GetIndString(filename, sFilenames, siResources);
  355.     if (OpenResFile(filename) != -1) {
  356.         foundresourcesfile = TRUE;
  357.     }
  358.     foundimagesfile = FALSE;
  359.     GetIndString(filename, sFilenames, siImages);
  360.     if (OpenResFile(filename) != -1) {
  361.         foundimagesfile = TRUE;
  362.     }
  363.     GetIndString(filename, sFilenames, siSounds);
  364.     if (OpenResFile(filename) != -1) {
  365.         /* (need to do anything special if sounds not found??) */
  366.     }
  367.     /* Note that we don't complain yet if the resource/image files are missing,
  368.        since we don't yet know whether we actually need anything from them.
  369.        (Images etc might be built into app or game module, for instance.) */
  370. }
  371.  
  372. int
  373. open_preferences()
  374. {
  375.     short vref;
  376.     long dirid;
  377.     OSErr err;
  378.     Str255 filename;
  379.  
  380.     GetIndString(filename, sFilenames, siPreferences);
  381.     /* (should do a gestalt check first) */
  382.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
  383.                    &vref, &dirid);
  384.     if (err != noErr) {
  385.         SysEnvRec se;
  386.  
  387.         err = SysEnvirons(1, &se);
  388.         if (err == noErr) {
  389.             vref = se.sysVRefNum;
  390.         } else {
  391.             vref = 0;
  392.         }
  393.         dirid = 0;
  394.     }
  395.     err = HCreate(vref, dirid, filename, XconqSignature, 'pref');
  396.     if (err == dupFNErr) err = noErr;
  397.     if (err == noErr) {
  398.         /* Ensure that the resource fork exists. */
  399.         HCreateResFile(vref, dirid, filename);
  400.         prefs_refnum = HOpenResFile(vref, dirid, filename, fsRdWrPerm);
  401.         if (prefs_refnum == -1)
  402.             err = -1;
  403.         if (ResError())
  404.             err = ResError();
  405.     }
  406.     return err;
  407. }
  408.  
  409. void
  410. close_preferences()
  411. {
  412.     CloseResFile(prefs_refnum);
  413.     prefs_refnum = -1;
  414. }
  415.  
  416. void
  417. get_preferences()
  418. {
  419.     int startlineno = 0, endlineno = 0;
  420.     char *name;
  421.     Obj *uispec, *rest, *bdg;
  422.     OSErr err;
  423.     Handle prefs;
  424.     extern int checkpointinterval;
  425.  
  426.     err = open_preferences();
  427.     if (err == noErr) {
  428.         UseResFile(prefs_refnum);
  429.         prefs = GetResource('XCpf', 128);
  430.         if (prefs != nil) {
  431.             uispec = read_form_from_string(copy_string(*prefs), &startlineno, &endlineno);
  432.             if (consp(uispec)
  433.                 && symbolp(car(uispec))
  434.                 && strcmp(c_string(car(uispec)), "mac") == 0) {
  435.                 for (rest = cdr(uispec); rest != lispnil; rest = cdr(rest)) {
  436.                     bdg = car(rest);
  437.                     if (symbolp(car(bdg))) {
  438.                         name = c_string(car(bdg));
  439.                         if (strcmp(name, "sound") == 0) {
  440.                             playsounds = c_number(cadr(bdg));
  441.                         } else if (strcmp(name, "default-draw-grid") == 0) {
  442.                             default_draw_grid = c_number(cadr(bdg));
  443.                         } else if (strcmp(name, "default-draw-names") == 0) {
  444.                             default_draw_names = c_number(cadr(bdg));
  445.                         } else if (strcmp(name, "checkpoint-interval") == 0) {
  446.                             checkpointinterval = c_number(cadr(bdg));
  447.                         } else {
  448.                         }
  449.                     }
  450.                 }
  451.             }
  452.         }
  453.         close_preferences();
  454.     }
  455. }
  456.  
  457. void
  458. save_preferences()
  459. {
  460.     int cur_refnum;
  461.     char prefbuf[300];
  462.     Obj *uispec, *bdg;
  463.     OSErr err;
  464.     Handle oldprefs, prefs;
  465.     extern int checkpointinterval;
  466.  
  467.     err = open_preferences();
  468.     if (err == noErr) {
  469.         uispec = lispnil;
  470.         bdg = cons(intern_symbol("checkpoint-interval"), cons(new_number(checkpointinterval), lispnil));
  471.         uispec = cons(bdg, uispec);
  472.         bdg = cons(intern_symbol("default-draw-grid"), cons(new_number(default_draw_grid), lispnil));
  473.         uispec = cons(bdg, uispec);
  474.         bdg = cons(intern_symbol("default-draw-names"), cons(new_number(default_draw_names), lispnil));
  475.         uispec = cons(bdg, uispec);
  476.         bdg = cons(intern_symbol("sound"), cons(new_number(playsounds), lispnil));
  477.         uispec = cons(bdg, uispec);
  478.         uispec = cons(intern_symbol("mac"), uispec);
  479.         sprintlisp(prefbuf, uispec);
  480.         /* Copy the string into a handle that will be used for the resource. */
  481.         prefs = NewHandle(strlen(prefbuf) + 1);
  482.         HLock(prefs);
  483.         strcpy(*prefs, prefbuf);
  484.         HUnlock(prefs);
  485.         cur_refnum = CurResFile();
  486.         UseResFile(prefs_refnum);
  487.         oldprefs = GetResource('XCpf', 128);
  488.         if (oldprefs != nil) {
  489.             RmveResource(oldprefs);
  490.             UpdateResFile(prefs_refnum);
  491.         }
  492.         AddResource(prefs, 'XCpf', 128, "\pXconq Preferences");
  493.         close_preferences();
  494.         UseResFile(cur_refnum);
  495.     }
  496. }
  497.  
  498. /* Since Mac programs effectively take over the entire machine, we depend on
  499.    this event loop to handle everything that might come along.  */
  500.  
  501. void
  502. event_loop()
  503. {
  504.     int done = FALSE;
  505.     Boolean gotevent;
  506.     Point mouse;
  507.     EventRecord    event;
  508.     RgnHandle cursorRgn;
  509.     short itemhit;
  510.     DialogPtr dialog;
  511.     extern WindowPtr playersetupwin;
  512.  
  513.     /* Figure out if the WaitNextEvent Trap is available. */
  514.     useWNE = (NGetTrapAddress(0x60, ToolTrap) != NGetTrapAddress(0x9f, ToolTrap));
  515.     /* Pass WNE an empty region the 1st time thru. */
  516.     cursorRgn = NewRgn();
  517.     /* Loop (almost) forever. */
  518.     while (!eventloopdone) {
  519.         /* Use WaitNextEvent if it is available, otherwise GetNextEvent. */
  520.         if (useWNE) {
  521.             get_global_mouse(&mouse);
  522.             adjust_cursor(mouse, cursorRgn);
  523.             gotevent = WaitNextEvent(everyEvent, &event, 0L, cursorRgn);
  524.         } else {
  525.             SystemTask();
  526.             gotevent = GetNextEvent(everyEvent, &event);
  527.         }
  528.         /* First decide if the event is for a dialog or is just any old event. */
  529.         if (FrontWindow() != nil && IsDialogEvent(&event)) {
  530.             /* Handle all the modeless dialogs here. */
  531.             if (playersetupwin != nil && playersetupwin == FrontWindow())
  532.               handle_player_setup_event(&event);
  533.             if (DialogSelect(&event, &dialog, &itemhit)) {
  534.                 if (dialog == instructionswin) {
  535.                     hit_instructions_dialog(dialog, itemhit, &event);
  536.                 }
  537.                 if (dialog == playersetupwin) {
  538.                     mouse = event.where;
  539.                     SetPort(playersetupwin);
  540.                     GlobalToLocal(&mouse);
  541.                     if (hit_player_setup_dialog(itemhit, mouse))
  542.                       launch_game_2();
  543.                 }
  544.                 /* If this was something like a key or click event, but wasn't handled
  545.                    by the dialog, we're done and can go wait for the next event.
  546.                    Otherwise, for null events, fall through to the usual handling. */
  547.                 if (gotevent)
  548.                   continue;
  549.             }
  550.         }
  551.         if (gotevent) {
  552.             /* Make sure we have the right cursor before handling the event. */
  553.             adjust_cursor(event.where, cursorRgn);
  554.             handle_event(&event);
  555.         } else if (!beforestart && !endofgame) {
  556.             /* On null events, give the kernel a chance to run something. */
  557.             /* DON'T automatically go to a watch cursor, since run_game often
  558.                returns very quickly.  Instead, long-running subroutines should
  559.                call back to put a watch cursor up. */
  560.             run_game(1);
  561.             maybe_select_next_unit();
  562.             /* If the game ended, force various changes in interaction. */
  563.             if (endofgame)
  564.               set_end_of_game_interaction_modes();
  565.         }
  566.     }
  567. }
  568.  
  569. void
  570. get_global_mouse(Point *mouse)
  571. {
  572.     EventRecord    evt;
  573.     
  574.     OSEventAvail(0, &evt);
  575.     *mouse = evt.where;
  576. }
  577.  
  578. void
  579. set_end_of_game_interaction_modes()
  580. {
  581.     Map *map;
  582.  
  583.     for_all_maps(map) {
  584.         map->moveonclick = map->autoselect = FALSE;
  585.         force_update(map->window);
  586.     }
  587. }
  588.  
  589. Point lastmouse;
  590.  
  591. char mouseoverbuf[100];
  592.  
  593. /* Change the cursor to reflect the context. */
  594.  
  595. void
  596. adjust_cursor(Point mouse, RgnHandle region)
  597. {
  598.     int x, y, approxdir = 1, usual = TRUE;
  599.     Unit *unit = NULL;
  600.     extern char *mouseover;
  601.     Map *map;
  602.     CursPtr adjust_designer_cursor();
  603.     GrafPtr oldport;
  604.  
  605.     map = map_from_window(FrontWindow());
  606.     if (map != NULL) {
  607.         GetPort(&oldport);
  608.         SetPort(map->window);
  609.         GlobalToLocal(&mouse);
  610.         if (mouse.h > conwid && mouse.h < map->window->portRect.right - sbarwid
  611.             && mouse.v > map->toph && mouse.v < map->window->portRect.bottom - sbarwid) {
  612.             if (map_modal != NO_MODAL) {
  613.                 switch (map_modal) {
  614.                     case ZOOM_MODAL:
  615.                         SetCursor(*opencrosscursor);
  616.                         break;
  617.                     case FIRE_MODAL:
  618.                     case FIRE_INTO_MODAL:
  619.                         SetCursor(*firecursor);
  620.                         break;
  621.                     default:
  622.                         run_error("unknown modal tool %d", map_modal);
  623.                         break;
  624.                 }
  625.                 usual = FALSE;
  626. #ifdef DESIGNERS
  627.             } else if (dside->designer && tooltype != notool) {
  628.                 SetCursor(adjust_designer_cursor(mouse, region));  usual = FALSE;
  629. #endif DESIGNERS
  630.             } else if (map->moveonclick) {
  631.                 if (map->numselections == 1
  632.                     && (unit = map->selections[0]) != NULL) {
  633.                     /* Calculate the approx dir to here from selected unit. */
  634.                     m_nearest_cell(map, mouse.h, mouse.v, &x, &y);
  635.                     /* Note that we allow x,y here to be outside the world. */
  636.                     if (mobile(unit->type)
  637.                         && (approxdir = approx_dir(x - unit->x, y - unit->y)) >= 0) {
  638.                         SetCursor(*(movecursors[approxdir]));  usual = FALSE;
  639.                     } else {
  640.                         SetCursor(*nomovecursor);  usual = FALSE;
  641.                     }
  642.                 } else if (map->numselections > 1) {
  643.                     SetCursor(*allmovecursor);  usual = FALSE;
  644.                 } else {
  645.                     /* (this is a little confusing here if no units are selected, since
  646.                        will just be arrow cursor) */
  647.                 }
  648.             }
  649.             /* This isn't really "cursor adjustment", but this is the right place
  650.                to do it - change the topline of the map to indicate what the cursor
  651.                is over. */
  652.             if (map->toph > 0 && !EqualPt(mouse, lastmouse)) {
  653.                 oneliner(map, mouse.h, mouse.v);
  654.                 if (strcmp(tmpbuf, mouseoverbuf) != 0) {
  655.                     strcpy(mouseoverbuf, tmpbuf);
  656.                     mouseover = mouseoverbuf;
  657.                     draw_top_line(map);
  658.                 }
  659.                 lastmouse = mouse;
  660.             }
  661.         } else {
  662.             if (map->toph > 0) {
  663.                 if (mouseover != NULL) {
  664.                     mouseover = NULL;
  665.                     draw_top_line(map);
  666.                 }
  667.             }
  668.         }
  669.         SetPort(oldport);
  670.     }
  671.     
  672.     if (endofgame || (!beforestart && dside && !dside->ingame)) {
  673.         SetCursor(*grayarrowcursor);
  674.         return;
  675.     }
  676.     /* If we got here and no cursor has been set already, go with the basic arrow. */
  677.     if (usual)
  678.       SetCursor(&QD(arrow));
  679. }
  680.  
  681. /* Decipher an event. */
  682.  
  683. int
  684. handle_event(EventRecord *event)
  685. {
  686.     short part, err, rslt = 0;
  687.     WindowPtr win;
  688.     char key;
  689.     Point pnt;
  690.  
  691.     switch (event->what) {
  692.         case mouseDown:
  693.             /* See if the click happened in a special part of the screen. */
  694.             part = FindWindow(event->where, &win);
  695.             switch (part) {
  696.                 case inMenuBar:
  697.                     adjust_menus();
  698.                     do_menu_command(MenuSelect(event->where));
  699.                     break;
  700.                 case inSysWindow:
  701.                     SystemClick(event, win);
  702.                     break;
  703.                 case inContent:
  704.                     if (win != FrontWindow()) {
  705.                         /* Bring the clicked-on window to the front. */
  706.                         SelectWindow(win);
  707.                         /* Fix the menu to match the new front window. */
  708.                         adjust_menus();
  709.                         /* We always want to discard the event now, since clicks in a
  710.                            windows are often irreversible actions. */
  711.                     } else {
  712.                         /* Mouse clicks in the front window do something useful. */
  713.                         do_mouse_down(win, event);
  714.                     }
  715.                     break;
  716.                 case inDrag:
  717.                     /* Standard drag behavior, no tricks necessary. */
  718.                     DragWindow(win, event->where, &dragrect);
  719.                     break;
  720.                 case inGrow:
  721.                     grow_window(win, event->where);
  722.                     break;
  723.                 case inZoomIn:
  724.                 case inZoomOut:
  725.                     zoom_window(win, event->where, part);
  726.                     break;
  727.                 case inGoAway:
  728.                     close_window(win);
  729.                     break;
  730.             }
  731.             break;
  732.         case keyDown:
  733.         case autoKey:
  734.             key = event->message & charCodeMask;
  735.             /* Check for menukey equivalents. */
  736.             if (event->modifiers & cmdKey) {
  737.                 if (event->what == keyDown) {
  738.                     adjust_menus();
  739.                     do_menu_command(MenuKey(key));
  740.                 }
  741.             } else {
  742.                 if (event->what == keyDown) {
  743.                     int handled = FALSE;
  744.  
  745.                     /* Random keypress, interpret it. */
  746.                     if (FrontWindow() == constructionwin)
  747.                       handled = do_key_down_construction(key);
  748.                     if (!handled)
  749.                       do_keyboard_command(key);
  750.                 }
  751.             }
  752.             break;
  753.         case activateEvt:
  754.             activate_window((WindowPtr) event->message, event->modifiers & activeFlag);
  755.             break;
  756.         case updateEvt:
  757.             update_window((WindowPtr) event->message);
  758.             break;
  759.         case diskEvt:
  760.             /*    Call DIBadMount in response to a diskEvt, so that the user can format
  761.                  a floppy. (from DTS Sample) */
  762.             if (HiWord(event->message) != noErr) {
  763.                 SetPt(&pnt, 50, 50);
  764.                 err = DIBadMount(pnt, event->message);
  765.             }
  766.             break;
  767.         case app4Evt:
  768.             /* Grab only a single byte. */
  769.             switch ((event->message >> 24) & 0xFF) {
  770.                 case 0xfa:
  771.                     break;
  772.                 case 1:
  773.                     inbackground = !(event->message & 1);
  774.                     activate_window(FrontWindow(), !inbackground);
  775.                     break;
  776.             }
  777.             break;
  778.         case kHighLevelEvent:
  779.             AEProcessAppleEvent(event);
  780.             break;
  781.         case nullEvent:
  782.             rslt = 1;
  783.             break;
  784.         default:
  785.             break;
  786.     }
  787. #ifdef DEBUGGING
  788.     /* This just forces output into the file. */
  789.     update_debugging();
  790. #endif
  791.     return rslt;
  792. }
  793.  
  794. /* Handle window growing by mindlessly tracking via GrowWindow,
  795.    then passing the chosen size to specific window resize handlers
  796.    or else doing the generic resize. */
  797.  
  798. void
  799. grow_window(WindowPtr win, Point where)
  800. {
  801.     long winsize;
  802.     short winh, winv;
  803.     GrafPtr oldport;
  804.  
  805.     if ((winsize = GrowWindow(win, where, &sizerect)) != 0) {
  806.         GetPort(&oldport);
  807.         SetPort(win);
  808.         winh = LoWord(winsize);  winv = HiWord(winsize);
  809.         if (map_from_window(win)) {
  810.             grow_map(map_from_window(win), winh, winv);
  811.         } else if (list_from_window(win)) {
  812.             grow_list(list_from_window(win), winh, winv);
  813.         } else if (win == historywin) {
  814.             grow_history(winh, winv);
  815.         } else if (win == constructionwin) {
  816.             grow_construction(winh, winv);
  817.         } else if (win == helpwin) {
  818.             grow_help(winh, winv);
  819.         }
  820.         SetPort(oldport);
  821.     }
  822. }
  823.  
  824. void
  825. zoom_window(WindowPtr win, Point where, int part)
  826. {
  827.     GrafPtr oldport;
  828.  
  829.     if (TrackBox(win, where, part)) {
  830.         GetPort(&oldport);
  831.         /* The window must be the current port. (ZoomWindow bug) */
  832.         SetPort(win);
  833.         if (map_from_window(win)) {
  834.             zoom_map(map_from_window(win), part);
  835.         } else if (list_from_window(win)) {
  836.             zoom_list(list_from_window(win), part);
  837.         } else if (win == constructionwin) {
  838.             zoom_construction(part);
  839.         } else if (win == historywin) {
  840.             zoom_history(part);
  841.         } else if (win == helpwin) {
  842.             zoom_help(part);
  843.         } else {
  844.             /* Generic window zooming. */
  845.             EraseRect(&win->portRect);
  846.             ZoomWindow(win, part, true);
  847.             InvalRect(&win->portRect);
  848.         }
  849.         SetPort(oldport);
  850.     }
  851. }
  852.  
  853. void
  854. close_window(WindowPtr win)
  855. {
  856.     if (is_da_window(win)) {
  857.         CloseDeskAcc(((WindowPeek) win)->windowKind);
  858.     } else if (is_app_window(win)) {
  859.         /* Remove from the windows menu (OK to call even if window not in menu). */
  860.         remove_window_menu_item(win);
  861.         /* Do special activities for some window subtypes. */
  862.         if (map_from_window(win)) {
  863.             destroy_map(map_from_window(win));
  864.         } else if (list_from_window(win)) {
  865.             destroy_list(list_from_window(win));
  866.         } else if (unit_closeup_from_window(win)) {
  867.             destroy_unit_closeup(unit_closeup_from_window(win));
  868. #ifdef DESIGNERS
  869.         } else if (win == designwin) {
  870.             /* Closing the design palette implies we're done designing. */
  871.             disable_designing();
  872. #endif /* DESIGNERS */
  873.         }
  874.         /* Remove the window from our sight, will provoke update events. */
  875.         HideWindow(win);
  876.         /* At least for now, don't actually dispose of the window. */
  877.     }
  878. }
  879.  
  880. /* This just dispatches to the appropriate window handler. */
  881.  
  882. void
  883. do_mouse_down(WindowPtr window, EventRecord *event)
  884. {
  885.     Point mouse;
  886.     Map *map;
  887.     List *list;
  888.     UnitCloseup *unitcloseup;
  889.  
  890.     if (is_app_window(window)) {
  891.         SetPort(window);
  892.         mouse = event->where;
  893.         GlobalToLocal(&mouse);
  894.         /* Locate the interface object that this is on. */
  895.         if ((map = map_from_window(window)) != NULL) {
  896.             do_mouse_down_map(map, mouse, event->modifiers); 
  897.         } else if ((list = list_from_window(window)) != NULL) {
  898.             do_mouse_down_list(list, mouse, event->modifiers); 
  899.         } else if ((unitcloseup = unit_closeup_from_window(window)) != NULL) {
  900.             do_mouse_down_unit_closeup(unitcloseup, mouse, event->modifiers); 
  901.         } else if (window == gamewin) {
  902.             do_mouse_down_game(mouse, event->modifiers);
  903.         } else if (window == historywin) {
  904.             do_mouse_down_history(mouse, event->modifiers);
  905.         } else if (window == constructionwin) {
  906.             do_mouse_down_construction(mouse, event->modifiers);
  907.         } else if (window == helpwin) {
  908.             do_mouse_down_help(mouse, event->modifiers);
  909. #ifdef DESIGNERS
  910.         } else if (window == designwin) {
  911.             do_mouse_down_design(mouse, event->modifiers);
  912. #endif /* DESIGNERS */
  913.         }
  914.     } else {
  915.         /* ??? */
  916.     }
  917. }
  918.  
  919. /* Bringing a window to the front may entail messing with the menu. */
  920.  
  921. void
  922. activate_window(WindowPtr win, int activate)
  923. {
  924.     Map *map;
  925.     List *list;
  926.  
  927.     if (win == nil) return;
  928.     if (activate) {
  929.         /* It's convenient to make the activated window also be the current GrafPort. */
  930.         SetPort(win);
  931.     }
  932.     adjust_menus();
  933.     if ((map = map_from_window(win)) != NULL) {
  934.         activate_map(map, activate);
  935.     } else if ((list = list_from_window(win)) != NULL) {
  936.         activate_list(list, activate);
  937.     } else if (win == constructionwin) {
  938.         activate_construction(activate);
  939.     } else if (win == helpwin) {
  940.         activate_help(activate);
  941.     } else {
  942.         DGprintf("%sactivating random window\n", (activate ? "" : "de"));
  943.     }
  944. }
  945.  
  946. /* Update a given window.  This is the main routine that causes drawing into
  947.    all the different kinds of windows. */
  948.  
  949. void
  950. update_window(WindowPtr win)
  951. {
  952.     int controls = TRUE, growbox = FALSE;
  953.     GrafPtr oldport;
  954.     Map *map;
  955.     List *list;
  956.     UnitCloseup *unitcloseup;
  957.  
  958.     /* Set the updating window to be the current grafport. */
  959.     GetPort(&oldport);
  960.     SetPort(win);
  961.     recalc_depths();
  962.     BeginUpdate(win);
  963.     if ((map = map_from_window(win)) != NULL) {
  964.         draw_map(map);
  965.         growbox = TRUE;
  966.     } else if ((list = list_from_window(win)) != NULL) {
  967.         draw_list(list);
  968.         growbox = TRUE;
  969.     } else if ((unitcloseup = unit_closeup_from_window(win)) != NULL) {
  970.         draw_unit_closeup(unitcloseup);
  971.     } else if (win == gamewin) {
  972.         draw_game();
  973.         controls = FALSE;
  974.     } else if (win == historywin) {
  975.         draw_history();
  976.         growbox = TRUE;
  977.     } else if (win == constructionwin) {
  978.         draw_construction();
  979.         growbox = TRUE;
  980.     } else if (win == helpwin) {
  981.         draw_help();
  982.         growbox = TRUE;
  983. #ifdef DESIGNERS
  984.     } else if (win == designwin) {
  985.         draw_design_window();
  986.         controls = FALSE;
  987. #endif /* DESIGNERS */
  988.     } else {
  989.         controls = FALSE;
  990.     }
  991.     if (controls) {
  992.         UpdateControls(win, win->visRgn);
  993.     }
  994.     if (growbox) {
  995.         DrawGrowIcon(win);
  996.     }
  997.     EndUpdate(win);
  998.     SetPort(oldport);
  999. }
  1000.  
  1001. static int last_tick_count = 0;
  1002.  
  1003. void
  1004. maybe_select_next_unit()
  1005. {
  1006.     Unit *unit;
  1007.     Map *map;
  1008.  
  1009.     if ((!beforestart && !endofgame)
  1010.         && (map = map_from_window(FrontWindow())) != NULL
  1011.         && map->autoselect) {
  1012.         /* Hunt around for a reasonable "next unit" to select. */
  1013.         /* Scroll over to the current unit if appropriate. */
  1014.         if (map->curunit != NULL
  1015.             && in_play(map->curunit)
  1016.             && (map->curunit->act
  1017.                 && map->curunit->act->acp > 0)  /* should be "above min"? */
  1018.             && (map->curunit->plan
  1019.                 && !map->curunit->plan->asleep
  1020.                 && !map->curunit->plan->reserve
  1021.                 && !map->curunit->plan->delayed)
  1022.             && map->scrolltocurunit
  1023.             ) {
  1024.             scroll_to_unit(map, map->curunit);
  1025.             map->scrolltocurunit = FALSE;
  1026.             goto blink;
  1027.         }
  1028.         unit = autonext_unit_inbox(dside, map->curunit, map->vp);
  1029.         if (unit
  1030.             && unit->plan
  1031.             && !unit->plan->asleep
  1032.             && !unit->plan->reserve
  1033.             && !unit->plan->delayed
  1034.             && unit->plan->waitingfortasks
  1035.             ) {
  1036.             map->curunit = unit;
  1037.             select_exactly_one_unit(map, map->curunit);
  1038.             goto blink;
  1039.         }
  1040.         /* Look for the next unit. */
  1041.         unit = find_next_awake_mover(dside, map->curunit);
  1042.         if (unit
  1043.             && unit->plan
  1044.             && !unit->plan->asleep
  1045.             && !unit->plan->reserve
  1046.             && !unit->plan->delayed
  1047.             && unit->plan->waitingfortasks
  1048.             ) {
  1049.             map->curunit = unit;
  1050.             select_exactly_one_unit(map, map->curunit);
  1051.             goto blink;
  1052.         }
  1053.         /* Start over from beginning of list. */
  1054.         unit = find_next_awake_mover(dside, NULL);
  1055.         if (unit
  1056.             && unit->plan
  1057.             && !unit->plan->asleep
  1058.             && !unit->plan->reserve
  1059.             && !unit->plan->delayed
  1060.             && unit->plan->waitingfortasks
  1061.             ) {
  1062.             map->curunit = unit;
  1063.             select_exactly_one_unit(map, map->curunit);
  1064.             goto blink;
  1065.         }
  1066.       blink:
  1067.         if (map->curunit != NULL) {
  1068.             int tick_count;
  1069.  
  1070.             tick_count = TickCount();
  1071.             if (tick_count - last_tick_count >= 10) {
  1072.                 last_tick_count = tick_count;
  1073.                 animation_pattern_state = (animation_pattern_state + 1) % 8;
  1074.                 draw_selection_animation(map, map->curunit);
  1075.             }
  1076.         }
  1077.     }
  1078. }
  1079.  
  1080. /* Used to check for any unread required parameters. Returns true if we
  1081.    missed at least one. */
  1082.  
  1083. Boolean
  1084. missed_any_parameters(AppleEvent *message)
  1085. {
  1086.     OSErr err;
  1087.     DescType ignoredActualType;
  1088.     AEKeyword missedKeyword;
  1089.     Size ignoredActualSize;
  1090.     EventRecord    event;
  1091.  
  1092.     err = AEGetAttributePtr(message, keyMissedKeywordAttr, typeKeyword, &ignoredActualType,
  1093.                             (Ptr) &missedKeyword, sizeof(missedKeyword), &ignoredActualSize);
  1094.     /* No error means that we found some unused parameters. */
  1095.     if (err == noErr) {
  1096.         event.message = *(long *) &ignoredActualType;
  1097.         event.where = *(Point *) &missedKeyword;
  1098.         err = errAEEventNotHandled;
  1099.     }
  1100.     /* errAEDescNotFound means that there are no more parameters.  If we get
  1101.        an error code other than that, flag it. */
  1102.     return (err != errAEDescNotFound);
  1103. }
  1104.  
  1105. static pascal OSErr
  1106. do_ae_open_application(AppleEvent *message, AppleEvent *reply, long refcon)
  1107. {
  1108. #pragma unused (message, refcon)
  1109.     OSErr err;
  1110.  
  1111.     if (splash_dialog() == diSplashQuit) {
  1112.         /* Set the global that lets the whole program exit. */
  1113.         eventloopdone = TRUE;
  1114.     }
  1115.     AEPutParamPtr(reply, keyReplyErr, typeShortInteger, (Ptr) &err, sizeof(short));
  1116.     return err;
  1117. }
  1118.  
  1119. /* Called when we receive an AppleEvent with an ID of "kAEOpenDocuments".
  1120.    This routine gets the direct parameter, parses it up into little FSSpecs,
  1121.    and opens the first indicated file.  It also shows the technique to be used in
  1122.    determining if you are doing everything the AppleEvent record is telling
  1123.    you.  Parameters can be divided up into two groups: required and optional.
  1124.    Before executing an event, you must make sure that you've read all the
  1125.    required events.  This is done by making an "any more?" call to the
  1126.    AppleEvent manager. */
  1127.  
  1128. static pascal OSErr
  1129. do_ae_open_documents(AppleEvent *message, AppleEvent *reply, long refcon)
  1130. {
  1131. #pragma unused (refcon)
  1132.  
  1133.     OSErr err, err2;
  1134.     AEDesc theDesc;
  1135.     FSSpec fsspec;
  1136.     short loop;
  1137.     long numFilesToOpen;
  1138.     AEKeyword ignoredKeyWord;
  1139.     DescType ignoredType;
  1140.     Size ignoredSize;
  1141.  
  1142.     theDesc.dataHandle = nil;
  1143.  
  1144.     err = AEGetParamDesc(message, keyDirectObject, typeAEList, &theDesc);
  1145.     if (err)
  1146.       return err;
  1147.     if (!missed_any_parameters(message)) {
  1148.         /* Got all the parameters we need.  Now, go through the direct object,
  1149.            see what type it is, and parse it up. */
  1150.         err = AECountItems(&theDesc, &numFilesToOpen);
  1151.         if (!err) {
  1152.             /* We have numFilesToOpen that need opening, as either a window
  1153.                or to be printed.  Go to it... */
  1154.             for (loop = 1; ((loop <= numFilesToOpen) && (!err)); ++loop) {
  1155.                 err = AEGetNthPtr(&theDesc, loop, typeFSS, &ignoredKeyWord, &ignoredType,
  1156.                                   (Ptr) &fsspec, sizeof(fsspec), &ignoredSize);
  1157.                 if (err)
  1158.                   break;
  1159.                 if (open_game_from_fsspec(&fsspec))
  1160.                   break;
  1161.             }
  1162.         }
  1163.     }
  1164.     err2 = AEDisposeDesc(&theDesc);
  1165.     err = (err ? err : err2);
  1166.     AEPutParamPtr(reply, keyReplyErr, typeShortInteger, (Ptr) &err, sizeof(short));
  1167.     return err;
  1168. }
  1169.  
  1170. static pascal OSErr
  1171. do_ae_print_documents(AppleEvent *message, AppleEvent *reply, long refcon)
  1172. {
  1173.     OSErr err;
  1174.  
  1175.     AEPutParamPtr(reply, keyReplyErr, typeShortInteger, (Ptr) &err, sizeof(short));
  1176.     return err;
  1177. }
  1178.  
  1179. static pascal OSErr
  1180. do_ae_quit_application(AppleEvent *message, AppleEvent *reply, long refcon)
  1181. {
  1182.     OSErr err = noErr;
  1183.  
  1184.     /* Set the global that lets the whole program exit. */
  1185.     eventloopdone = TRUE;
  1186.     AEPutParamPtr(reply, keyReplyErr, typeShortInteger, (Ptr) &err, sizeof(short));
  1187.     return noErr;
  1188. }
  1189.  
  1190. static pascal OSErr
  1191. do_ae_join_game(AppleEvent *message, AppleEvent *reply, long refcon)
  1192. {
  1193. #pragma unused (message, refcon)
  1194.     OSErr err = noErr;
  1195.  
  1196.     if (playersetupwin != nil) {
  1197.         add_remote_player(NULL);
  1198.     } else {
  1199.         beep();
  1200.         beep();
  1201.         beep();
  1202.     }
  1203.     return err;
  1204. }
  1205.  
  1206. /* A warning just gets displayed, no other action is taken. */
  1207.  
  1208. void
  1209. low_init_warning(char *str)
  1210. {
  1211.     Str255 buf;
  1212.  
  1213.     if (suppresswarnings) return;
  1214.     /* Cursor may be weird from loading, reset it. */
  1215.     SetCursor(&QD(arrow));
  1216.     c2p(str, buf);
  1217.     ParamText(buf, "\p", "\p", "\p");
  1218.     switch (CautionAlert(aInitWarning, nil)) {
  1219.         case 1:
  1220.             /* Just keep going, hope that warning was a false alarm. */
  1221.             if (0 /* option key or some such */) {
  1222.                 suppresswarnings = TRUE;
  1223.             }
  1224.             break;
  1225.         case 2:
  1226.             /* It would be better to undo everything and blast back to initial choices,
  1227.                but that would be pretty hard to implement, and should be a rare occurrence
  1228.                anyway. */
  1229.             ExitToShell();
  1230.             break;
  1231.     }
  1232. }
  1233.  
  1234. /* An init error is not necessarily fatal, but we still have to start over. */
  1235.  
  1236. void
  1237. low_init_error(char *str)
  1238. {
  1239.     Str255 buf;
  1240.  
  1241.     /* Cursor may be weird from loading, reset it. */
  1242.     SetCursor(&QD(arrow));
  1243.     c2p(str, buf);
  1244.     ParamText(buf, "\p", "\p", "\p");
  1245.     StopAlert(aInitError, nil);
  1246.     /* This is a bad time to choke, no way to recover.  Fortunately,
  1247.        it's not a big loss, since there's no game yet to lose,
  1248.        and so we can just exit directly. */
  1249.     ExitToShell();
  1250. }
  1251.  
  1252. /* Runtime warnings are for when it's important to bug the players,
  1253.    but doesn't necessarily mean imminent danger of a crash. */
  1254.  
  1255. void
  1256. low_run_warning(char *str)
  1257. {
  1258.     Str255 buf;
  1259.  
  1260.     if (suppresswarnings) return;
  1261.     /* If we're not actually in the game yet, make an init warning instead. */
  1262.     if (beforestart) {
  1263.         low_init_warning(str);
  1264.         return;
  1265.     }
  1266.     c2p(str, buf);
  1267.     ParamText(buf, "\p", "\p", "\p");
  1268.     switch (CautionAlert(aRunWarning, nil)) {
  1269.         case 1:
  1270.             if (0 /* option key?? */) {
  1271.                 suppresswarnings = TRUE;
  1272.             }
  1273.             break;
  1274.         case 2:
  1275.             save_the_game(TRUE, TRUE);
  1276.             ExitToShell();
  1277.             break;
  1278.         case 3:
  1279.             /* Just blast out of here. */
  1280.             ExitToShell();
  1281.             break;
  1282.     }
  1283. }
  1284.  
  1285. /* An run error is fatal, but allow an emergency save, might be able to salvage. */
  1286.  
  1287. void
  1288. low_run_error(char *str)
  1289. {
  1290.     Str255 buf;
  1291.  
  1292.     /* If we're not actually in the game yet, make an init error instead. */
  1293.     if (beforestart) {
  1294.         low_init_error(str);
  1295.         return;
  1296.     }
  1297.     /* Make some space available, in case this is a memory exhaustion error. */
  1298.     if (spare != nil) {
  1299.         DisposHandle(spare);
  1300.         spare = nil;
  1301.     }
  1302.     c2p(str, buf);
  1303.     ParamText(buf, "\p", "\p", "\p");
  1304.     switch (StopAlert(aRunError, nil)) {
  1305.         case 1:
  1306.             break;
  1307.         case 2:
  1308.             save_the_game(TRUE, TRUE);
  1309.             break;
  1310.     }
  1311.     /* We're outta here - just ahead of scrambled heaps and dangling ptrs! */
  1312.     ExitToShell();
  1313. }
  1314.  
  1315. /* This is true when a side has a display that may be safely written to. */
  1316.  
  1317. int
  1318. active_display(Side *side)
  1319. {
  1320.     return (side && side->ui && side->ui->active);
  1321. }
  1322.  
  1323. /* The Mac never has any display buffers to flush. */
  1324.  
  1325. void
  1326. flush_display_buffers(Side *side)
  1327. {
  1328. }
  1329.  
  1330. /* Detect types of windows. */
  1331.  
  1332. int
  1333. is_da_window(WindowPtr win)
  1334. {
  1335.     return (win != nil && ((WindowPeek) win)->windowKind < 0);
  1336. }
  1337.  
  1338. int
  1339. is_app_window(WindowPtr win)
  1340. {
  1341.     return (win != nil && ((WindowPeek) win)->windowKind >= 0);
  1342. }
  1343.  
  1344. void
  1345. notify(Side *side, char *format, ...)
  1346. {
  1347.     va_list ap;
  1348.     char tmpnbuf[BUFSIZE];
  1349.  
  1350.     if (active_display(side)) {
  1351.         va_start(ap, format);
  1352.         vsprintf(tmpnbuf, format, ap);
  1353.         va_end(ap);
  1354.         /* Always capitalize first char of notice. */
  1355.         if (islower(tmpnbuf[0])) tmpnbuf[0] = toupper(tmpnbuf[0]);
  1356.         /* (should do something with the result!) */
  1357.     }
  1358. }
  1359.  
  1360. /* Kernel callback to update info about the given side. */
  1361.  
  1362. void
  1363. update_side_display(Side *side, Side *side2, int rightnow)
  1364. {
  1365.     GrafPtr oldport;
  1366.     extern int mayseeall;
  1367.  
  1368.     if (active_display(side) && side2 != NULL) {
  1369.         GetPort(&oldport);
  1370.         if (gamewin != nil && ((WindowPeek) gamewin)->visible) {
  1371.             SetPort(gamewin);
  1372.             draw_side_status(side2);
  1373.         }
  1374.         SetPort(oldport);
  1375.         if (side2 == dside && !side->ingame && side->status == 0 && wasingame) {
  1376.             /* (should be able to quit from here?) */
  1377.             CautionAlert(aOutOfGame, nil); 
  1378.             wasingame = FALSE;
  1379.             mayseeall = TRUE;
  1380.         }
  1381.     }
  1382. }
  1383.  
  1384. /* Kernel callback to show the current turn. */
  1385.  
  1386. void
  1387. update_turn_display(Side *side, int rightnow)
  1388. {
  1389.     GrafPtr oldport;
  1390.     Map *map;
  1391.     extern char *curseasonname;
  1392.  
  1393.     if (active_display(side)) {
  1394.         strcpy(curdatestr + 1, absolute_date_string(g_turn()));
  1395.         if (curseasonname != NULL) {
  1396.             strcat(curdatestr + 1, " (");
  1397.             strcat(curdatestr + 1, curseasonname);
  1398.             strcat(curdatestr + 1, ")");
  1399.         }
  1400.         curdatestr[0] = strlen(curdatestr + 1);
  1401.         GetPort(&oldport);
  1402.         if (gamewin != nil && ((WindowPeek) gamewin)->visible) {
  1403.             SetPort(gamewin);
  1404.             draw_game_date();
  1405.         }
  1406.         for_all_maps(map) {
  1407.             if (map->toph > 0) {
  1408.                 SetPort(map->window);
  1409.                 draw_top_line(map);
  1410.             }
  1411.         }
  1412.         SetPort(oldport);
  1413.     }
  1414. }
  1415.  
  1416. /* Callback that gets run once after all turn setup is done but before any movement. */
  1417.  
  1418. void
  1419. update_action_display(Side *side, int rightnow)
  1420. {
  1421.     GrafPtr oldport;
  1422.     Map *map;
  1423.     List *list;
  1424.     UnitCloseup *unitcloseup;
  1425.  
  1426.     if (active_display(side)) {
  1427.         GetPort(&oldport);
  1428.         for_all_maps(map) {
  1429.             draw_selections(map);
  1430.             if (map->autoselect) {
  1431.                 unselect_all(map);
  1432.                 map->curunit = NULL;
  1433.             }
  1434.         }
  1435.         for_all_lists(list) {
  1436.             reorganize_list(list);
  1437.         }
  1438.         for_all_unit_closeups(unitcloseup) {
  1439.             force_update(unitcloseup->window);
  1440.         }
  1441.         SetPort(oldport);
  1442.     }
  1443. }
  1444.  
  1445. void
  1446. update_action_result_display(Side *side, Unit *unit, int rslt, int rightnow)
  1447. {
  1448.     Action *action;
  1449.  
  1450.     if (active_display(side)) {
  1451.         DGprintf("%s %s result is %s\n",
  1452.                 unit_desig(unit),
  1453.                 action_desig((unit->act ? &(unit->act->nextaction) : NULL)),
  1454.                 hevtdefns[rslt].name);
  1455.         /* (should handle errors specially) */
  1456.         action = (unit->act ? &(unit->act->nextaction) : NULL);
  1457.         if (action == NULL) return;
  1458.         switch (action->type) {
  1459.             case A_CREATE_IN:
  1460.             case A_CREATE_AT:
  1461.             case A_BUILD:
  1462.                 if (rslt == A_ANY_DONE) {
  1463.                     update_construction_type_list();
  1464.                 }
  1465.                 break;
  1466.         }
  1467.     }
  1468. }
  1469.  
  1470. void
  1471. update_event_display(Side *side, HistEvent *hevt, int rightnow)
  1472. {
  1473.     int data0;
  1474.     extern int mayseeall;
  1475.  
  1476.     data0 = hevt->data[0];
  1477.     if (active_display(side)) {
  1478.         switch (hevt->type) {
  1479.           case H_SIDE_LOST:
  1480.             if (side->id == data0) {
  1481.                 lost_game_dialog();
  1482.                 told_outcome = TRUE;
  1483.                 mayseeall = TRUE;
  1484.             }
  1485.               break;
  1486.           case H_SIDE_WON:
  1487.             if (side->id == data0) {
  1488.                 won_game_dialog();
  1489.                 told_outcome = TRUE;
  1490.                 mayseeall = TRUE;
  1491.             }
  1492.               break;
  1493.           case H_GAME_ENDED:
  1494.             /* Don't display the generic game-end dialog if we already
  1495.                got a lost or won dialog. */
  1496.               if (!told_outcome)
  1497.                 game_over_dialog();
  1498.             mayseeall = TRUE;
  1499.             break;
  1500.           default:
  1501.               /* No special display for these types of events. */
  1502.               break;
  1503.         }
  1504.         /* Also work on the history window if it's up. */
  1505.         if (historywin != nil) {
  1506.             update_history_window(hevt);
  1507.         }
  1508.     }
  1509. }
  1510.  
  1511. void
  1512. update_fire_at_display(Side *side, Unit *unit, Unit *unit2, int m, int rightnow)
  1513. {
  1514.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  1515.     int startticks, innerticks;
  1516.     Map *map;
  1517.     GrafPtr oldport, curport = NULL;
  1518.  
  1519.     if (active_display(side)) {
  1520.         GetPort(&oldport);
  1521.         startticks = TickCount();
  1522.         i = 0;
  1523.         /* Tweak the pen modes of all the maps. */
  1524.         for_all_maps(map) {
  1525.             SetPort(map->window);
  1526.             PenMode(patXor);
  1527.             if (map->vp->hw > 10) PenSize(2, 2);
  1528.             else PenSize(1, 1);
  1529.         }
  1530.         while (TickCount() < startticks + 32) {
  1531.             innerticks = TickCount();
  1532.             for_all_maps(map) {
  1533.                 if (curport != map->window) {
  1534.                     SetPort(map->window);
  1535.                     curport = map->window;
  1536.                 }
  1537.                 m_xform_unit_self(map, unit, &sx1, &sy1, &sw1, &sh1);
  1538.                 m_xform_unit_self(map, unit2, &sx2, &sy2, &sw2, &sh2);
  1539.                 /* Offset to draw lines from the middle of the units' images. */
  1540.                 sx1 += sw1 / 2;  sy1 += sh1 / 2;
  1541.                 sx2 += sw2 / 2;  sy2 += sh2 / 2;
  1542.                 /* Draw one segment of a line between the units. */
  1543.                 dx = (sx2 - sx1) / 4;  dy = (sy2 - sy1) / 4;
  1544.                 xx = sx1 + ((i / 2) % 4) * dx;  yy = sy1 + ((i / 2) % 4) * dy;
  1545.                 MoveTo(xx, yy);  Line(dx, dy);
  1546.             }
  1547.             while (TickCount() < innerticks + 1); /* 2 here seems a bit slowish */
  1548.             ++i;
  1549.         }
  1550.         /* Restore the pen modes of all the maps. */
  1551.         for_all_maps(map) {
  1552.             SetPort(map->window);
  1553.             PenNormal();
  1554.         }
  1555.         SetPort(oldport);
  1556.     }
  1557. }
  1558.  
  1559. void
  1560. update_fire_into_display(Side *side, Unit *unit, int x2, int y2, int z2, int m, int rightnow)
  1561. {
  1562.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  1563.     int startticks, innerticks;
  1564.     Map *map;
  1565.     GrafPtr oldport, curport = NULL;
  1566.  
  1567.     if (active_display(side)) {
  1568.         GetPort(&oldport);
  1569.         startticks = TickCount();
  1570.         i = 0;
  1571.         /* Tweak the pen modes of all the maps. */
  1572.         for_all_maps(map) {
  1573.             SetPort(map->window);
  1574.             PenMode(patXor);
  1575.             if (map->vp->hw > 10) PenSize(2, 2);
  1576.             else PenSize(1, 1);
  1577.         }
  1578.         while (TickCount() < startticks + 32) {
  1579.             innerticks = TickCount();
  1580.             for_all_maps(map) {
  1581.                 if (curport != map->window) {
  1582.                     SetPort(map->window);
  1583.                     curport = map->window;
  1584.                 }
  1585.                 m_xform_unit_self(map, unit, &sx1, &sy1, &sw1, &sh1);
  1586.                 xform(map, x2, y2, &sx2, &sy2);
  1587.                 sw2 = map->vp->hw;  sh2 = map->vp->hh;
  1588.                 /* Offset to draw lines from the middle of the units' images. */
  1589.                 sx1 += sw1 / 2;  sy1 += sh1 / 2;
  1590.                 sx2 += sw2 / 2;  sy2 += sh2 / 2;
  1591.                 /* Draw one segment of a line between the units. */
  1592.                 dx = (sx2 - sx1) / 4;  dy = (sy2 - sy1) / 4;
  1593.                 xx = sx1 + ((i / 2) % 4) * dx;  yy = sy1 + ((i / 2) % 4) * dy;
  1594.                 MoveTo(xx, yy);  Line(dx, dy);
  1595.             }
  1596.             while (TickCount() < innerticks + 1); /* 2 here seems a bit slowish */
  1597.             ++i;
  1598.         }
  1599.         /* Restore the pen modes of all the maps. */
  1600.         for_all_maps(map) {
  1601.             SetPort(map->window);
  1602.             PenNormal();
  1603.         }
  1604.         SetPort(oldport);
  1605.     }
  1606. }
  1607.  
  1608. /* Update any displayed info about the given unit. */
  1609.  
  1610. void
  1611. update_unit_display(Side *side, Unit *unit, int rightnow)
  1612. {
  1613.     UnitCloseup *unitcloseup;
  1614.  
  1615.     if (active_display(side) && unit != NULL) {
  1616.         if (side != unit->side) update_unit_in_maps(unit);
  1617.         if (1 /* unit visible to side in any way */ && inside_area(unit->x, unit->y)) {
  1618.             update_cell_display(side, unit->x, unit->y, TRUE);
  1619.         }
  1620.         update_unit_in_lists(unit);
  1621.         if ((unitcloseup = find_unit_closeup(unit)) != NULL
  1622.             && 1 /* window is visible */) {
  1623.             draw_unit_closeup(unitcloseup);
  1624.         }
  1625.         if (unit->side != NULL && unit->act != NULL) {
  1626.             update_side_display(side, unit->side, rightnow);
  1627.         }
  1628.         if (constructionwin != nil
  1629.             && ((WindowPeek) constructionwin)->visible) {
  1630.             update_construction_unit_list(unit);
  1631.         }
  1632.     }
  1633. }
  1634.  
  1635. void
  1636. update_unit_acp_display(Side *side, Unit *unit, int rightnow)
  1637. {
  1638.     UnitCloseup *unitcloseup;
  1639.  
  1640.     if (active_display(side) && unit != NULL) {
  1641. #if 0  /* maybe add later, maybe not - acp change not usually visible tho */
  1642.         if (side != unit->side) update_unit_in_maps(unit);
  1643.         if (1 /* unit visible to side in any way */ && inside_area(unit->x, unit->y)) {
  1644.             update_cell_display(side, unit->x, unit->y, TRUE);
  1645.         }
  1646. #endif
  1647.         update_unit_in_lists(unit);
  1648.         unitcloseup = find_unit_closeup(unit);
  1649.         if (unitcloseup != NULL && (((WindowPeek) (unitcloseup->window))->visible)) {
  1650.             draw_unit_closeup(unitcloseup);
  1651.         }
  1652.     }
  1653. }
  1654.  
  1655. void
  1656. update_unit_in_maps(Unit *unit)
  1657. {
  1658.     Map *map;
  1659.  
  1660.     if (side_controls_unit(dside, unit))
  1661.       return;
  1662.     for_all_maps(map) {
  1663.         unselect_unit_on_map(map, unit);
  1664.     }
  1665. }
  1666.  
  1667. void
  1668. update_clock_display(Side *side, int rightnow)
  1669. {
  1670.     GrafPtr oldport;
  1671. #if 0
  1672.     Map *map;
  1673. #endif
  1674.     time_t now;
  1675.     extern time_t lastnow;
  1676.  
  1677.     if (active_display(side)) {
  1678.         time(&now);
  1679.         /* If no changes since the last draw, jump out of here. */
  1680.         if (now == lastnow)
  1681.           return;
  1682.         GetPort(&oldport);
  1683.         if (gamewin != nil && ((WindowPeek) gamewin)->visible) {
  1684.             SetPort(gamewin);
  1685.             draw_game_clocks();
  1686.         }
  1687. #if 0  /* no clock display in the topline yet */
  1688.         for_all_maps(map) {
  1689.             if (map->toph > 0) {
  1690.                 SetPort(map->window);
  1691.                 draw_top_line(map);
  1692.             }
  1693.         }
  1694. #endif
  1695.         SetPort(oldport);
  1696.     }
  1697. }
  1698.  
  1699. void
  1700. update_all_progress_displays(char *str, int s)
  1701. {
  1702.     GrafPtr oldport;
  1703.     extern char *game_progress_str;
  1704.  
  1705.     if (!active_display(dside))
  1706.       return;
  1707.     game_progress_str = str;
  1708.     GetPort(&oldport);
  1709.     if (gamewin != nil && ((WindowPeek) gamewin)->visible) {
  1710.         SetPort(gamewin);
  1711.         draw_game_progress();
  1712.     }
  1713.     SetPort(oldport);
  1714. }
  1715.  
  1716. /* Bring up a modal dialog saying that the player has won. */
  1717.  
  1718. void
  1719. won_game_dialog()
  1720. {
  1721.     int done = FALSE;
  1722.     short ditem;
  1723.     WindowPtr win;
  1724.  
  1725.     win = GetNewDialog(dWinGame, NULL, (DialogPtr) -1L);
  1726.     ShowWindow(win);
  1727.     while (!done) {
  1728.         draw_default_button(win, diWinGameQuit);
  1729.         SetCursor(&QD(arrow));
  1730.         ModalDialog(NULL, &ditem);
  1731.         switch (ditem) {
  1732.             case diWinGameQuit:
  1733.                 ExitToShell();
  1734.                 break;
  1735.             case diWinGameContinue:
  1736.                 done = TRUE;
  1737.                 break;
  1738.             default:
  1739.                 break;
  1740.         }
  1741.     }
  1742.     DisposDialog(win);
  1743. }
  1744.  
  1745. /* Bring up a modal dialog saying that the player has lost. */
  1746.  
  1747. void
  1748. lost_game_dialog()
  1749. {
  1750.     int done = FALSE;
  1751.     short ditem;
  1752.     WindowPtr win;
  1753.     extern int forcedtoresign;
  1754.  
  1755.     /* If quitting required a resignation, the player just
  1756.        wants out, with no dwelling on final position, so
  1757.        bypass the dialog here. */
  1758.     if (forcedtoresign) {
  1759.         ExitToShell();
  1760.         return;
  1761.     }
  1762.     win = GetNewDialog(dLoseGame, NULL, (DialogPtr) -1L);
  1763.     ShowWindow(win);
  1764.     while (!done) {
  1765.         draw_default_button(win, diLoseGameQuit);
  1766.         SetCursor(&QD(arrow));
  1767.         ModalDialog(NULL, &ditem);
  1768.         switch (ditem) {
  1769.             case diLoseGameQuit:
  1770.                 ExitToShell();
  1771.                 break;
  1772.             case diLoseGameContinue:
  1773.                 done = TRUE;
  1774.                 break;
  1775.             default:
  1776.                 break;
  1777.         }
  1778.     }
  1779.     DisposDialog(win);
  1780. }
  1781.  
  1782. /* Bring up a modal dialog saying that the game has ended, with no
  1783.    implication of the player having either won or lost. */
  1784.  
  1785. void
  1786. game_over_dialog()
  1787. {
  1788.     int done = FALSE;
  1789.     short ditem;
  1790.     WindowPtr win;
  1791.  
  1792.     win = GetNewDialog(dGameOver, NULL, (DialogPtr) -1L);
  1793.     ShowWindow(win);
  1794.     while (!done) {
  1795.         draw_default_button(win, diGameOverQuit);
  1796.         SetCursor(&QD(arrow));
  1797.         ModalDialog(NULL, &ditem);
  1798.         switch (ditem) {
  1799.             case diGameOverQuit:
  1800.                 ExitToShell();
  1801.                 break;
  1802.             case diGameOverContinue:
  1803.                 done = TRUE;
  1804.                 break;
  1805.             default:
  1806.                 break;
  1807.         }
  1808.     }
  1809.     DisposDialog(win);
  1810. }
  1811.  
  1812. /* Update the displays to reflect the arrival of a message from another
  1813.    side. */
  1814.  
  1815. void
  1816. update_message_display(Side *side, Side *sender, char *str, int rightnow)
  1817. {
  1818.     int done = FALSE;
  1819.     short ditem;
  1820.     Str255 tmpstr;
  1821.     WindowPtr win;
  1822.     short itemtype;  Handle itemhandle;  Rect itemrect;
  1823.  
  1824.     if (active_display(side)) {
  1825.         if (str == NULL)
  1826.           str = "";
  1827.         if (1 /* chose to make a dialog */) {
  1828.             win = GetNewDialog(dMessageReceive, NULL, (DialogPtr) -1L);
  1829.             GetDItem(win, diMessageReceiveText, &itemtype, &itemhandle, &itemrect);
  1830.             c2p(str, tmpstr);
  1831.             SetIText(itemhandle, tmpstr);
  1832.             ShowWindow(win);
  1833.             while (!done) {
  1834.                 draw_default_button(win, diMessageReceiveOK);
  1835.                 SetCursor(&QD(arrow));
  1836.                 ModalDialog(NULL, &ditem);
  1837.                 switch (ditem) {
  1838.                     case diMessageReceiveOK:
  1839.                         done = TRUE;
  1840.                         break;
  1841.                     default:
  1842.                         break;
  1843.                 }
  1844.             }
  1845.             DisposDialog(win);
  1846.         } else {
  1847.             notify(side, "From %s: %s", side_desig(sender), str);
  1848.         }
  1849.     }
  1850. }
  1851.  
  1852. /* Support for movie display. */
  1853.  
  1854. MovieType movie_types[10];
  1855.  
  1856. struct a_movie {
  1857.   enum movie_type type;
  1858.   int args[5];
  1859. };
  1860.  
  1861. int numscheduled;
  1862.  
  1863. struct a_movie movies[10];
  1864.  
  1865. int
  1866. schedule_movie(Side *side, enum movie_type movie, ...)
  1867. {
  1868.     int i;
  1869.     va_list ap;
  1870.  
  1871.     if (numscheduled >= 10)
  1872.       return FALSE;
  1873.     if (side != dside)
  1874.       return FALSE;
  1875.     memset(&(movies[numscheduled]), 0, sizeof(struct a_movie));
  1876.     movies[numscheduled].type = movie;
  1877.     va_start(ap, movie);
  1878.     for (i = 0; i < 6; ++i)
  1879.       movies[numscheduled].args[i] = va_arg(ap, long);
  1880.     va_end(ap);
  1881.     ++numscheduled;
  1882.     return TRUE;
  1883. }
  1884.  
  1885. void
  1886. play_movies(SideMask sidemask)
  1887. {
  1888.     int i, j;
  1889.     int sx, sy, sw, sh;
  1890.     int startticks, innerticks;
  1891.     Str255 sndnamebuf;
  1892.     Map *map;
  1893.     Unit *unit;
  1894.     GrafPtr oldport, curport = NULL;
  1895.     Handle sound;
  1896.     extern int numsoundplays;
  1897.     long startcount;
  1898.  
  1899.     if (1 /* anything visible */) {
  1900.         GetPort(&oldport);
  1901.         startticks = TickCount();
  1902.         i = 0;
  1903.         /* Tweak the pen modes of all the maps. */
  1904.         for_all_maps(map) {
  1905.             SetPort(map->window);
  1906.             PenMode(patXor);
  1907.             if (map->vp->hw > 10) PenSize(2, 2);
  1908.             else PenSize(1, 1);
  1909.         }
  1910.         while (TickCount() < startticks + 32) {
  1911.             innerticks = TickCount();
  1912.             for (j = 0; j < numscheduled; ++j) {
  1913.                 if ((movies[j].type == movie_miss
  1914.                      || movies[j].type == movie_hit
  1915.                      || movies[j].type == movie_death
  1916.                      || movies[j].type == movie_extra_0)
  1917.                     && playsounds
  1918.                     && numsoundplays < 3) {
  1919.  
  1920.                     if (movies[j].type == movie_extra_0) {
  1921.                         c2p((char *) movies[j].args[0], sndnamebuf);
  1922.                         sound = GetNamedResource('snd ', sndnamebuf);
  1923.                     } else {
  1924.                         sound = GetNamedResource('snd ', "\pcrunch");
  1925.                     }
  1926.                     if (sound != nil) {
  1927.                         SndPlay(nil, sound, false);
  1928.                         ReleaseResource(sound);
  1929.                     }
  1930.                     ++numsoundplays;
  1931.                 }
  1932.                 switch (movies[j].type) {
  1933.                     case movie_null:
  1934.                         break;
  1935.                     case movie_miss:
  1936.                         unit = (Unit *) movies[j].args[0];
  1937.                         for_all_maps(map) {
  1938.                             if (curport != map->window) {
  1939.                                 SetPort(map->window);
  1940.                                 curport = map->window;
  1941.                             }
  1942.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1943.                             draw_blast_image(map->window, sx, sy, sw, sh, 0);
  1944.                         }
  1945.                         startcount = TickCount();
  1946.                         /* Delay for part of a second (should relinquish cpu tho) */
  1947.                         while ((TickCount() - startcount) < 10);
  1948.                         for_all_maps(map) {
  1949.                             if (curport != map->window) {
  1950.                                 SetPort(map->window);
  1951.                                 curport = map->window;
  1952.                             }
  1953.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1954.                             clear_blast_image(map->window, sx, sy, sw, sh, 2);
  1955.                         }
  1956.                         break;
  1957.                     case movie_hit:
  1958.                         unit = (Unit *) movies[j].args[0];
  1959.                         for_all_maps(map) {
  1960.                             if (curport != map->window) {
  1961.                                 SetPort(map->window);
  1962.                                 curport = map->window;
  1963.                             }
  1964.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1965.                             draw_blast_image(map->window, sx, sy, sw, sh, 1);
  1966.                         }
  1967.                         startcount = TickCount();
  1968.                         sound = nil;
  1969.                         if (playsounds) {
  1970.                             sound = GetNamedResource('snd ', "\pboom");
  1971.                         }
  1972.                         if (sound != nil) {
  1973.                             SndPlay(nil, sound, false);
  1974.                             ReleaseResource(sound);
  1975.                         }
  1976.                         /* Delay for part of a second (should relinquish cpu tho) */
  1977.                         while ((TickCount() - startcount) < 10);
  1978.                         for_all_maps(map) {
  1979.                             if (curport != map->window) {
  1980.                                 SetPort(map->window);
  1981.                                 curport = map->window;
  1982.                             }
  1983.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1984.                             clear_blast_image(map->window, sx, sy, sw, sh, 2);
  1985.                         }
  1986.                         break;
  1987.                     case movie_death:
  1988.                         unit = (Unit *) movies[j].args[0];
  1989.                         for_all_maps(map) {
  1990.                             if (curport != map->window) {
  1991.                                 SetPort(map->window);
  1992.                                 curport = map->window;
  1993.                             }
  1994.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1995.                             draw_blast_image(map->window, sx, sy, sw, sh, 2);
  1996.                         }
  1997.                         startcount = TickCount();
  1998.                         sound = nil;
  1999.                         if (playsounds) {
  2000.                             sound = GetNamedResource('snd ', "\pboom");
  2001.                         }
  2002.                         if (sound != nil) {
  2003.                             SndPlay(nil, sound, false);
  2004.                             ReleaseResource(sound);
  2005.                         }
  2006.                         /* Delay for part of a second (should relinquish cpu tho) */
  2007.                         while ((TickCount() - startcount) < 10);
  2008.                         for_all_maps(map) {
  2009.                             if (curport != map->window) {
  2010.                                 SetPort(map->window);
  2011.                                 curport = map->window;
  2012.                             }
  2013.                             m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  2014.                             clear_blast_image(map->window, sx, sy, sw, sh, 2);
  2015.                         }
  2016.                         break;
  2017.                     case movie_nuke:
  2018.                         break;
  2019.                     default:
  2020.                         break;
  2021.                 }
  2022.             }
  2023.             while (TickCount() < innerticks + 32);
  2024.             ++i;
  2025.         }
  2026.         /* Restore the pen modes of all the maps. */
  2027.         for_all_maps(map) {
  2028.             SetPort(map->window);
  2029.             PenNormal();
  2030.         }
  2031.         SetPort(oldport);
  2032.     }
  2033.     numscheduled = 0;
  2034. }
  2035.  
  2036. /* Move the window to a position staggered from the given last position. */
  2037.  
  2038. void
  2039. stagger_window(WindowPtr win, int *lasthp, int *lastvp)
  2040. {
  2041.     int h, v, retry = 0;
  2042.     Rect winrect;
  2043.     GrafPtr oldport;
  2044.  
  2045.     if (*lasthp > 0 && *lastvp > 0) {
  2046.         while (1) {
  2047.             h = *lasthp + 20;  v = *lastvp + 20;
  2048.             /* Let windows go partly off the screen, but keep at least the top
  2049.                40x40 pixels visible. */
  2050.             if (!position_on_screen(h + 40, v + 40)) {
  2051.                 if (retry < 20) {
  2052.                     h = retry * 20; v = 40;
  2053.                     ++retry;
  2054.                 } else {
  2055.                     /* This is getting out of hand - just pick something. */
  2056.                     h = 20;  v = 40;
  2057.                     break;
  2058.                 }
  2059.             } else if (!position_already_used(h, v)) {
  2060.                 break;
  2061.             }
  2062.         }
  2063.         MoveWindow(win, h, v, FALSE);
  2064.         *lasthp = h;  *lastvp = v;
  2065.     } else {
  2066.         /* Don't move the first window, but do record its position. */
  2067.         GetPort(&oldport);
  2068.         SetPort(win);
  2069.         winrect = win->portRect;
  2070.         LocalToGlobal(&top_left(winrect));
  2071.         *lasthp = winrect.left;  *lastvp = winrect.top;
  2072.         SetPort(oldport);
  2073.     }
  2074. }
  2075.  
  2076. int
  2077. position_on_screen(int h, int v)
  2078. {
  2079.     Point pnt;
  2080.     GDHandle screen = GetDeviceList();
  2081.     
  2082.     pnt.h = h;  pnt.v = v;
  2083.     for (screen = GetDeviceList(); screen != nil; screen = GetNextDevice(screen)) {
  2084.         if (TestDeviceAttribute(screen, screenDevice)
  2085.             && TestDeviceAttribute(screen, screenActive)) {
  2086.             if (PtInRect(pnt, &((*screen)->gdRect)))
  2087.               return TRUE;
  2088.         }
  2089.     }
  2090.     return FALSE;
  2091. }
  2092.  
  2093. /* (should make this more efficient?) */
  2094.  
  2095. int
  2096. position_already_used(int h, int v)
  2097. {
  2098.     int i;
  2099.     Rect winrect;
  2100.     WindowPtr win;
  2101.     GrafPtr oldport;
  2102.     extern int numwindows;
  2103.     extern WindowPtr *winmenuwins;
  2104.  
  2105.     for (i = 0; i < numwindows; ++i) {
  2106.         win = winmenuwins[i];
  2107.         GetPort(&oldport);
  2108.         SetPort(win);
  2109.         winrect = win->portRect;
  2110.         LocalToGlobal(&top_left(winrect));
  2111.         SetPort(oldport);
  2112.         if (h == winrect.left && v == winrect.top)
  2113.           return TRUE;
  2114.     }
  2115.     return FALSE;
  2116. }
  2117.  
  2118.  
  2119. GDHandle
  2120. best_zoom_screen(Rect *rectptr)
  2121. {
  2122.     int greatestarea = 0, sectarea;
  2123.     Rect srect;
  2124.     GDHandle screen = GetDeviceList(), bestscreen = GetMainDevice();
  2125.  
  2126.     while (screen != nil) {
  2127.         if (TestDeviceAttribute(screen, screenDevice)
  2128.             && TestDeviceAttribute(screen, screenActive)) {
  2129.             SectRect(rectptr, &((*screen)->gdRect), &srect);
  2130.             sectarea = (srect.right - srect.left) * (srect.bottom - srect.top);
  2131.             if (sectarea > greatestarea) {
  2132.                 greatestarea = sectarea;
  2133.                 bestscreen = screen;
  2134.             }
  2135.         }
  2136.         screen = GetNextDevice(screen);
  2137.     }
  2138.     return bestscreen;
  2139. }
  2140.  
  2141. void
  2142. set_standard_state(WindowPtr win, int fullw, int fullh)
  2143. {
  2144.     int screenw, screenh, wintitlehgt, mbaradj = 0;
  2145.     Rect winrect, gdrect, zoomrect;
  2146.     GDHandle bestscreen;
  2147.  
  2148.     if (!hasColorQD) {
  2149.         zoomrect = QD(screenBits).bounds;
  2150.         InsetRect(&zoomrect, 4, 4);
  2151.     } else {
  2152.         winrect = win->portRect;
  2153.         LocalToGlobal((Point *) &(winrect.top));
  2154.         LocalToGlobal((Point *) &(winrect.bottom));
  2155.         wintitlehgt = winrect.top - 1 - (*(((WindowPeek) win)->strucRgn))->rgnBBox.top;
  2156.         /* Get the best screen to zoom on. */
  2157.         bestscreen = best_zoom_screen(&winrect);
  2158.         gdrect = (*bestscreen)->gdRect;
  2159.         /* Adjust to the actual subarea that we can use. */
  2160.         if (bestscreen == GetMainDevice()) {
  2161.             gdrect.top += GetMBarHeight();
  2162.         }
  2163.         InsetRect(&gdrect, 3, 3);
  2164.         gdrect.top += wintitlehgt;
  2165.         screenw = gdrect.right - gdrect.left;  screenh = gdrect.bottom - gdrect.top;
  2166.         if (winrect.left + fullw <= gdrect.right
  2167.             && winrect.top + fullh <= gdrect.bottom) {
  2168.             SetRect(&zoomrect, winrect.left, winrect.top, winrect.left + fullw, winrect.top + fullh);
  2169.         } else if (fullw <= screenw || fullh <= screenh) {
  2170.             SetRect(&zoomrect, gdrect.left, gdrect.top, gdrect.left + fullw, gdrect.top + fullh);
  2171.             if (fullw > screenw) zoomrect.right = gdrect.right;
  2172.             if (fullh > screenh) zoomrect.bottom = gdrect.bottom;
  2173.         } else {
  2174.             zoomrect = gdrect;
  2175.         }
  2176.     }
  2177.     ((WStateDataPtr) *(((WindowPeek) win)->dataHandle))->stdState = zoomrect;
  2178. }
  2179.  
  2180. /* General routine to outline the given item of a given dialog. */
  2181.  
  2182. void
  2183. draw_default_button(DialogPtr dialog, short ditem)
  2184. {
  2185.     GrafPtr oldport;
  2186.     short itemtype;  Handle itemhandle;  Rect itemrect;
  2187.  
  2188.     GetPort(&oldport);
  2189.     SetPort(dialog);
  2190.     GetDItem(dialog, ditem, &itemtype, &itemhandle, &itemrect);
  2191.     PenSize(3, 3);
  2192.     InsetRect(&itemrect, -4, -4);
  2193.     FrameRoundRect(&itemrect, 16, 16);
  2194.     PenNormal();
  2195.     SetPort(oldport);
  2196. }
  2197.  
  2198. char *
  2199. get_string_from_item(Handle itemhandle)
  2200. {
  2201.     char tmpbuf[BUFSIZE];
  2202.     Str255 tmpstr;
  2203.     
  2204.     GetIText(itemhandle, tmpstr);
  2205.     p2c(tmpstr, tmpbuf);
  2206.     return copy_string(tmpbuf);
  2207. }
  2208.  
  2209. /* Cause an update of a window's entire contents. */
  2210.  
  2211. void
  2212. force_update(WindowPtr win)
  2213. {
  2214.     GrafPtr oldport;
  2215.  
  2216.     if (win == nil) return;
  2217.     GetPort(&oldport);
  2218.     SetPort(win);
  2219.     EraseRect(&win->portRect);
  2220.     InvalRect(&win->portRect);
  2221.     SetPort(oldport);
  2222. }
  2223.  
  2224. void
  2225. force_overall_update()
  2226. {
  2227.     Map *map;
  2228.     List *list;
  2229.     UnitCloseup *unitcloseup;
  2230.  
  2231.     force_update(gamewin);
  2232.     force_update(historywin);
  2233.     force_update(constructionwin);
  2234.     force_update(helpwin);
  2235.     for_all_maps(map) {
  2236.         force_update(map->window);
  2237.     }
  2238.     for_all_lists(list) {
  2239.         force_update(list->window);
  2240.     }
  2241.     for_all_unit_closeups(unitcloseup) {
  2242.         force_update(unitcloseup->window);
  2243.     }
  2244. }
  2245.  
  2246. void
  2247. beep()
  2248. {
  2249.     SysBeep(20);
  2250. }
  2251.  
  2252. void
  2253. update_everything()
  2254. {
  2255.     if (active_display(dside)) {
  2256.         force_overall_update();
  2257.     }
  2258. }
  2259.  
  2260. /* Set the type and creator of the file to be what is expected
  2261.    for a game design. */
  2262.  
  2263. void
  2264. set_game_file_type(char *name)
  2265. {
  2266.     FileParam pb;
  2267.     Str255 tmpstr;
  2268.     
  2269.     c2p(name, tmpstr);
  2270.     pb.ioNamePtr = tmpstr;
  2271.     pb.ioVRefNum = 0;
  2272.     pb.ioFVersNum = 0;
  2273.     pb.ioFDirIndex = 0;
  2274.     if (PBGetFInfoSync((ParmBlkPtr) &pb) == noErr) {
  2275.         pb.ioFlFndrInfo.fdType = 'TEXT';
  2276.         pb.ioFlFndrInfo.fdCreator = XconqSignature;
  2277.         PBSetFInfoSync((ParmBlkPtr) &pb);
  2278.     }
  2279. }
  2280.